#include "FailoverUtils.h"
#include "htsc_consumer_intf.h"
#include "htsc_common/htsc_grpc_utils.h"
#include "htsc_registry/url.h"


//int FailoverUtils::m_switchThreshold = 5;
//int FailoverUtils::m_punishTime = 60;

#define CONSUMERID_PROVIDERID_SEPARATOR "@"
#define TEN_MINUTES_IN_MILLIS 600000

FailoverUtils::FailoverUtils()
{
}


FailoverUtils::~FailoverUtils()
{
}

void FailoverUtils::setSwitchThreshold(int _switchThreshold) {
	m_switchThreshold = _switchThreshold;
}

void FailoverUtils::setPunishTime(int _punishTime) {
	m_punishTime = _punishTime;
}
void FailoverUtils::record_provider_failure(char* consumerId, char* providerId) {
	if (!consumerId || !providerId)
	{
		return;
	}
	std::string key = std::string(consumerId) + CONSUMERID_PROVIDERID_SEPARATOR + providerId;
	uint64_t currentTimestamp = htsc_get_timestamp_in_mills();
	uint64_t lastTimestamp = 0;
	std::map<std::string, uint64_t>::iterator lastFailingTimeIter = m_lastFailingTimestamp.find(key.c_str());
	if (lastFailingTimeIter != m_lastFailingTimestamp.end())
	{
		lastTimestamp = lastFailingTimeIter->second;
	}
	else {
		lastTimestamp = currentTimestamp;
	}

	m_lastFailingTimestamp[key] = currentTimestamp;

	updateFailingProviders(consumerId, providerId);

	updateFailTimes(consumerId, providerId, lastTimestamp, currentTimestamp);
}
void FailoverUtils::updateFailingProviders(char* consumerId, char* providerId) {
	if (!consumerId || !providerId)
	{
		return;
	}
	std::map<std::string, std::vector<std::string> >::iterator providersMapIter = m_failingProviders.find(consumerId);
	if (providersMapIter == m_failingProviders.end())
	{
		std::vector<std::string> providers;
		m_failingProviders.insert(std::pair<std::string, std::vector<std::string> >(consumerId, providers));
		providersMapIter = m_failingProviders.find(consumerId);
	}
	bool bFind = false;
	for (size_t i = 0; i < providersMapIter->second.size(); i++)
	{
		if (0 == strcmp(providerId, providersMapIter->second[i].c_str()))
		{
			bFind = true;
			break;
		}
	}
	if (!bFind)
	{
		providersMapIter->second.push_back(providerId);
	}
}
void FailoverUtils::updateFailTimes(char* consumerId, char* providerId, int64_t lastTimestamp, int64_t currentTimestamp) {
	if (!consumerId || !providerId)
	{
		return;
	}
	std::string key = std::string(consumerId) + CONSUMERID_PROVIDERID_SEPARATOR + providerId;
	std::map<std::string, gpr_atm>::iterator requestFailureIter = m_requestFailures.find(key);
	if (requestFailureIter == m_requestFailures.end())
	{
		m_requestFailures.insert(std::pair<std::string, int>(key, 0));
		requestFailureIter = m_requestFailures.find(key);
	}
	// 为了提高性能，不对调用成功的情况进行记录，使用以下策略近似判断连续多次调用失败：
	// 将当前时间和最后一次出错时间的记录做比较，如果时间间隔大于10分钟，将之前的错误次数清0
	if (currentTimestamp - lastTimestamp > TEN_MINUTES_IN_MILLIS)
	{
		gpr_atm_rel_store(&requestFailureIter->second, 0);
	}
	gpr_atm_full_fetch_add(&requestFailureIter->second, 1);
	url_t* consumerUrl = url_parse(consumerId);
	if (!consumerUrl)
	{
		return;
	}
	int consumerLBProviderAmount = get_consumer_lb_providers_acount(consumerUrl->path);
	int consumerProvidersSum = get_consumer_providers_acount(consumerUrl->path);

	if (gpr_atm_acq_load(&requestFailureIter->second) > m_switchThreshold)
	{
		if (consumerLBProviderAmount == 1)//只存在一个可用provider，并且调用还失败了
		{
			if (m_punishTime != 0)
			{
				m_removeProviderTimestamp[consumerId] = currentTimestamp;
				set_provider_failover_flag(consumerUrl->path, providerId);
			}
		}
		else if(consumerLBProviderAmount > 1){//存在多个provider的情况，可以选择其他provider
			set_provider_failover_flag(consumerUrl->path, providerId);
		}
		gpr_atm_rel_store(&requestFailureIter->second,0);
	}
	consumerLBProviderAmount = get_consumer_lb_providers_acount(consumerUrl->path);
	if (consumerLBProviderAmount == 0 && consumerProvidersSum > 0 && m_punishTime > 0)
	{
		uint64_t removeTime = 0;
		std::map<std::string, uint64_t>::iterator removeProviderTimestamIter = m_removeProviderTimestamp.find(consumerId);
		if (removeProviderTimestamIter != m_removeProviderTimestamp.end())
		{
			removeTime = removeProviderTimestamIter->second;
		}
		if (currentTimestamp - removeTime > m_punishTime)
		{
			// 重新查询一遍服务提供者，将注册中心上的服务列表写入当前消费者的服务列表
			get_all_providers_by_name(consumerUrl->path);
			// 然后将removeProviderTimestamp中的这个消费者的数据删除
			m_removeProviderTimestamp.erase(consumerId);
		}
	}

	url_free(consumerUrl);
	FREE_PTR(consumerUrl);
}